﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Threading;
using SerialPortTester;

namespace SensorClient
{
    public class SensorPandaOld : SensorConsumer
    {
        protected const int readerBufferSize = 256;
        protected char[] reader = new char[readerBufferSize];
        protected int readerCursor = 0;
        protected int readerChars = 0;
        protected int readerStart = 0;
        protected bool doAcq = false;
        protected bool debugLevel = false;

        #region SupportMethods
        // Extrage valoarea dintre tagurile senzorului. Sterge inregistrarea din buffer.
        // Returneaza:
        // - true in cazul in care gaseste tagul din parametri, si stringul reply completat
        // - false in caz ca nu il gaseste
        protected bool ExtractReply(string first, string second, out string reply)
        {
            reply = "";
            bool reset = false;
            bool retValue = false;
            string readerString = new string(reader);
            int firstIndex = readerString.IndexOf(first);
            int secondIndex = readerString.IndexOf(second);
            if ((firstIndex != -1) & (secondIndex != -1))
            {
                if (secondIndex > firstIndex)
                {
                    // Extract reply
                    reply = readerString.Substring(firstIndex + first.Length, secondIndex - firstIndex - first.Length);

                    // Erase this reply from buffer
                    int offset = (first.Length + second.Length + reply.Length);
                    for (int i = firstIndex; i < offset; i++)
                    {
                        reader[i] = reader[i + offset];
                    }
                    for (int i = firstIndex + offset; i < readerString.Length; i++)
                    {
                        reader[i] = '\0';
                    }
                    //reader = (readerString.Substring(0, firstIndex) + readerString.Substring(secondIndex + second.Length)).ToCharArray();
                    readerCursor = readerCursor - offset;
                    if (readerCursor < 0)
                    {
                        reset = true;
                    }
                    retValue = true;
                }
                else
                {
                    reset = true;
                }

                if (reset)
                {
                    // Invalid input sequence found, need to flush buffer to recover
                    // Force acquisition
                    reader = new char[readerBufferSize];
                    readerCursor = 0;
                    doAcq = true;
                }
            }
            return retValue;
        }


        // Detecteaza modulul PSAssist pe porturile de tip serial
        // Returneaza null in caz ca nu gaseste modulul sau un string continand portul
        protected string DetectPSAssist()
        {
            string[] ports = SerialPort.GetPortNames();
            SerialPort SPort2;
            string PSAssistPort = null;

            // List all serial ports found if debugging
            if (debugLevel)
            {
                Console.Write("Serial ports found:");
                foreach (string port in ports)
                {
                    // Open Port and see if we receive data
                    Console.Write(" " + port);
                }
                Console.WriteLine();
            }

            foreach (string port in ports)
            {
                if (debugLevel)
                {
                    Console.WriteLine("Trying port " + port);
                }

                SPort2 = new SerialPort(port);
                SPort2.Open();
                if (SPort2.IsOpen)
                {
                    // Detect PSAssist Module
                    Thread.Sleep(4000);
                    SPort2.ReadTimeout = 500;
                    try
                    {
                        readerChars = SPort2.Read(reader, readerCursor, readerBufferSize - readerCursor);
                        readerCursor = readerChars;
                    }
                    catch (System.TimeoutException e)
                    {
                        continue;
                    }

                    string reply;
                    if (ExtractReply("<id>", "</id>\r\n", out reply))
                    {
                        PSAssistPort = port;
                        Console.WriteLine("Detected PSAssist module on port " + PSAssistPort);
                    }
                    SPort2.Close();
                    SPort2.Dispose();
                }

                // Found first port, use it!
                if (PSAssistPort != null)
                {
                    break;
                }
            }
            return PSAssistPort;
        }
        #endregion SupportMethods

        #region Constructors
        /// <summary>
        /// Creates a SensorPanda object
        /// </summary>
        /// <param name="packageInterval">Acquisition period of the package</param>
        public SensorPandaOld(int packageInterval)
            : base(packageInterval)
        {
            //This should be empty
        }
        #endregion Constructors

        /// <summary>
        /// The method is used by the package generation thread.It will
        /// generate an event every <param name="packageInterval"></param>
        /// seconds of which args contain the package values.
        /// </summary>
        protected override void DoAcquistion()
        {
            Console.WriteLine("SensorPanda thread started");
            const int writerBufferSize = 256;
            SerialPort SPort;
            char[] writer = new char[writerBufferSize];
            bool doAcq = false;
            int countAcq = 0;
            int ticker = 0;
            string PSAssistPort = null;
            bool packageSent = true;
            bool newFoto = false;
            bool newRef = false;
            int tmp = 0;
            int index = 0;

            PSAssistPort = DetectPSAssist();

            if (PSAssistPort != null)
            {
                SPort = new SerialPort(PSAssistPort);
                SPort.ReadTimeout = 5000;
                SerialPortFixer.Execute(PSAssistPort);
                SPort.Open();
                GC.SuppressFinalize(SPort);
                GC.SuppressFinalize(SPort.BaseStream);
            }
            else
            {
                SPort = null;
            }

            while (this.isOn == true)
            {
                if (SPort != null)
                {
                    if (SPort.IsOpen)
                    {
                        // Building receiver buffer
                        try
                        {
                            readerChars = SPort.Read(reader, readerCursor, readerBufferSize - readerCursor);
                            readerCursor += readerChars;
                        }
                        catch (System.TimeoutException e)
                        {
                            Console.WriteLine("Timeout occurred");
                            Console.ReadLine();
                            break;
                        }

                        // Safety measure
                        // If buffer is overfilling, reset it
                        if (readerCursor > 200)
                        {
                            SPort.DiscardInBuffer();
                            reader = new char[readerBufferSize];
                            readerCursor = 0;
                            doAcq = true;
                        }

                        // Print debugging info if in debugging mode
                        if (debugLevel)
                        {
                            Console.WriteLine("Ticker=" + ticker);
                            string print = new string(reader);
                            print.Replace('\n', '.');
                            print.Replace('\r', '.');
                            Console.WriteLine("reader=" + print);
                            Console.WriteLine("readerCursor=" + readerCursor);
                        }

                        // String magic
                        // Find out if we can find a reply in the buffer
                        string reply;
                        if (ExtractReply("<foto>", "</foto>\r\n", out reply))
                        {
                            Console.WriteLine("Acq=" + countAcq + " Retrieved value of foto: " + reply);
                            newFoto = true;
                        }
                        if (ExtractReply("<ref>", "</ref>\r\n", out reply))
                        {
                            Console.WriteLine("Acq=" + countAcq + " Retrieved value of ref: " + reply);
                            newRef = true;
                        }
                        if (ExtractReply("<id>", "</id>\r\n", out reply))
                        {
                            Console.WriteLine("Received heartbeat with id: " + reply);
                        }

                        // Is it time to acquire sensor data?
                        if (doAcq)
                        {
                            newFoto = false;
                            newRef = false;
                            packageSent = false;
                            SPort.Write("foto;");
                            SPort.Write("ref;");
                            countAcq++;
                            doAcq = false;
                        }

                        // If all sensors have been collected, assemble package and send event
                        if (newFoto && newRef && (!packageSent))
                        {
                            Console.WriteLine("Sending package to server");
                            packageSent = true;
                            #region BuildSensorPackage
                            //EKG
                            for (index = (int)PackageFormat.SensorPositionStart.ECG;
                                 index < PackageFormat.ECG_VECTOR_LENGTH
                                         * PackageFormat.ECG_ELEMENT_SIZE;
                                 index += 2)
                            {
                                tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                                package[index] = (byte)(tmp & (byte)0xFF);
                                package[index + 1] = (byte)(tmp >> 8);
                            }

                            //HTA
                            index = (int)PackageFormat.SensorPositionStart.HTA;
                            //Primary value
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);
                            //Secondary value
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index + 2] = (byte)(tmp & (byte)0xFF);
                            package[index + 3] = (byte)(tmp >> 8);

                            //Pulse
                            index = (int)PackageFormat.SensorPositionStart.PULSE;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Body temperature
                            index = (int)PackageFormat.SensorPositionStart.BODY_TEMPERATURE;
                            //Fixed part
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.EIGHT_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            //Decimal parth
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.FOUR_BITS_MAX);
                            package[index + 1] = (byte)tmp;

                            //Weight
                            index = (int)PackageFormat.SensorPositionStart.WEIGHT;
                            //Fixed part
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);
                            //Decimal part
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.FOUR_BITS_MAX);
                            package[index + 2] = (byte)(tmp & (byte)0xFF);

                            //Glicemia
                            index = (int)PackageFormat.SensorPositionStart.GLICEMY;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Respiratory frequency
                            index = (int)PackageFormat.SensorPositionStart.RESPIRATORY_FREQUENCY;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.EIGHT_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);

                            //Ambient temperature
                            index = (int)PackageFormat.SensorPositionStart.AMBIENT_TEMPERATURE;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Humidity sensor
                            index = (int)PackageFormat.SensorPositionStart.HUMIDITY;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //Light sensor
                            index = (int)PackageFormat.SensorPositionStart.LIGHT;
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.TEN_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            package[index + 1] = (byte)(tmp >> 8);

                            //RFID, Voltage, Gas
                            index = (int)PackageFormat.SensorPositionStart.RFID_VOLTAGE_GAS;
                            /*
                            How the boolean values are generated:
                            7 6 5 4 3   2       1          0
                            * * * * * GAS_BIT VOLTAGE_BIT RFID_BIT
                            */
                            tmp = rGen.Next((int)PackageFormat.BitMaxValues.THREE_BITS_MAX);
                            package[index] = (byte)(tmp & (byte)0xFF);
                            #endregion BuildSensorPackage
                        }

                    }
                    else
                    {
                        Console.WriteLine("Port is closed");
                    }
                    ticker++;
                    if (ticker > 5)
                    {
                        doAcq = true;
                        ticker = 0;
                    }
                    Thread.Sleep(500);

                } // if (SPort != null)
            } // while 
            if (SPort.IsOpen)
            {
                GC.ReRegisterForFinalize(SPort.BaseStream);
                GC.ReRegisterForFinalize(SPort);
                SPort.Close();
                SPort.Dispose();
            }

        }
    }
}
